package com.gitee.feizns.dynamic.reflect;

import com.gitee.feizns.dynamic.Objs;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 类型操作工具类
 * @author feizns
 * @since 2019/5/15
 */
public abstract class Classes {

    /**
     * <b>获取参数列表的类型列表</b>
     * @param params 参数列表
     * @return 返回参数对应的类型列表
     */
    public static List<Class<?>> of(Object... params) {
        return java.util.Arrays.stream(params).map(Object::getClass).collect(Collectors.toList());
    }

    /**
     * <b>获取一个类的继承链（从子类开始到父类）</b>
     * @param type {@link Class}
     * @return 返回参数type继承链上的所有类
     */
    public static Deque<Class<?>> of(Class<?> type) {
        Deque<Class<?>> deque = new ArrayDeque<>();
        while ( type != null ) {
            deque.offer(type);
            type = type.getSuperclass();
        }
        return deque;
    }

    /**
     * <b>获取一个类所实现的所有接口，依次向上延伸</b>
     * <p>（仅能获取在当前类中显式声明的接口列表（不能获取到父类所实现的接口））</p>
     * <p>
     *     获取一个类的所实现的所有接口（包括其接口所继承的接口）<br/>
     * </p>
     * 如果一个类的父类实现了{@code java.lang.Cloneable}而其自身没有实现该接口则，
     * 该方法不会列出{@code java.lang.Cloneable}接口
     * @param type {@link Class}
     * @return 返回显式声明的接口所实现的所有接口列表
     */
    public static Deque<Class<?>> ofInterfaces(Class<?> type) {
        Deque<Class<?>> result = new ArrayDeque<>();
        //参数type的接口列表
        Deque<Class<?>> interfaces = new ArrayDeque<>(java.util.Arrays.asList(type.getInterfaces()));
        //类似BFS遍历一个类所实现的所有接口
        while ( !interfaces.isEmpty() ) {
            Class<?> curt = interfaces.remove();
            result.offer(curt);
            interfaces.addAll(java.util.Arrays.asList(curt.getInterfaces()));
        }
        return result;
    }

    /**
     * <b>获取一个类的继承链和相应类的所有接口 当前类 -> 父类，当前类接口，父类接口</b>
     * <pre>{@code
     *     static class Father implements Cloneable{ }
     *     static class Son extends Father implements Serializable { }
     *     public static void main(String[] args) {
     *         //Son.class,Father.class,Object.class,Serializable.class,Cloneable.class
     *         System.out.println(getClassesAndInterfaces(Son.class));
     *     }
     * }</pre>
     * @param type {@link Class}
     * @return 返回从当前类到顶部的所有类及其相应的所有接口
     */
    public static Deque<Class<?>> ofClassesAndInterfaces(Class<?> type) {
        //先获取一个类的继承链
        Deque<Class<?>> classes = of(type);
        Set<Class<?>> interfaceSet = new HashSet<>(classes);
        //再获取继承链上每个类的所有接口
        classes.stream().map(Classes::ofInterfaces)
                .flatMap(Deque::stream)
                .filter(tmp -> !interfaceSet.contains(tmp))
                .peek(interfaceSet::add)
                .forEach(classes::add);
        return classes;
    }

    /**
     * <b>获取一个类的继承链和相应类的所有接口 当前类 -> 父类，当前类接口，父类接口</b>
     * @see #ofClassesAndInterfaces(Class)
     * <pre>{@code
     *     static class Father implements Cloneable{ }
     *     static class Son extends Father implements Serializable { }
     *     public static void main(String[] args) {
     *         //Son.class,Father.class,Object.class,Serializable.class,Cloneable.class
     *         System.out.println(getClassesAndInterfaces(Son.class));
     *     }
     * }</pre>
     * @param type {@link Class}
     * @return 返回从当前类到顶部的所有类及其相应的所有接口
     */
    public static Deque<Class<?>> ofInheritanceTree(Class<?> type) {
        return ofClassesAndInterfaces(type);
    }

    /**
     * <b>获取所继承的父类的泛型列表</b>
     * <pre>{@code
     *     class Father<T> {}
     *     class Son extends Father<String> {}
     *     public static void main(String[] args) {
     *         //[class java.lang.String]
     *         System.out.println(Arrays.toString(getSuperclassGenericTypes(Son.class)));
     *     }
     * }</pre>
     * @param type {@link Class}
     * @return 返回type类所显式写明的类型列表
     */
    public static Class<?>[] ofSuperclassGenericTypes(Class<?> type) {
        return ofGenericTypes(type.getGenericSuperclass());
    }

    /**
     * <b>获取一个类上所实现的接口及相应的所有的泛型类型</b>
     * <p>
     *      key 接口<br/>
     *      value 对应接口的泛型
     * </p>
     * <pre>{@code
        static abstract class A implements Comparable<String>, Collection<Integer> { }
        public static void main(String[] args) {
            //{Comparable<String>=[String.class], Collection<Integer>=[Integer.class]}
            System.out.println(getInterfacesGenericTypes(A.class));
        }
     * }</pre>
     * <pre>{@code
     * }</pre>
     * @param type {@link Class}
     * @return key为接口，value为接口上的泛型
     */
    public static Map<Type, Class<?>[]> ofInterfacesGenericTypes(Class<?> type) {
        Map<Type, Class<?>[]> ret = new LinkedHashMap<>();
        java.util.Arrays.stream(type.getGenericInterfaces()).forEach(inter -> ret.put(inter, ofGenericTypes(inter)));
        return ret;
    }

    /**
     * <b>获取类上的指定接口的泛型类型</b>
     * <pre>{@code
     *     static abstract class A implements Comparable<String>, Collection<Integer> { }
     *     public static void main(String[] args) {
     *         //[class java.lang.String]
     *         System.out.println(Arrays.toString(getInterfaceGenericTypes(A.class, Comparable.class)));
     *         //[class java.lang.Integer]
     *         System.out.println(Arrays.toString(getInterfaceGenericTypes(A.class, Collection.class)));
     *     }
     * }</pre>
     * @param clz {@link Class}
     * @param inter 接口类型
     * @return 返回对应接口的泛型列表
     */
    public static Class<?>[] ofInterfaceGenericTypes(Class<?> clz, Class<?> inter) {
        return java.util.Arrays.stream(clz.getGenericInterfaces())
                .filter(type -> type instanceof ParameterizedType && ((ParameterizedType) type).getRawType() == inter)
                .findFirst()
                .map(Classes::ofGenericTypes)
                .orElse(new Class[0]);
    }

    /**
     * <b>获取类型上的泛型列表</b>
     * <pre>{@code
        private static Map<Boolean, Object> map;
        public static Map<String, Object> get(Map<Integer, Object> map) { return null; }
        public static void main(String[] args) throws Exception {
            Method method = ClassUtils.class.getMethod("get", Map.class);
            Field field = ClassUtils.class.getDeclaredField("map");

            //[class java.lang.String, class java.lang.Object]
            System.out.println(Arrays.toString(getGenericTypes(method.getGenericReturnType())));

            //[class java.lang.Integer, class java.lang.Object]
            System.out.println(Arrays.toString(getGenericTypes(method.getGenericParameterTypes()[0])));

            //[class java.lang.Boolean, class java.lang.Object]
            System.out.println(Arrays.toString(getGenericTypes(field.getGenericType())));
        }
     * }</pre>
     * @param type {@link Type}
     * @return 返回对应的泛型列表
     */
    public static Class<?>[] ofGenericTypes(Type type) {
        return (type instanceof ParameterizedType)
                ? Objs.cast(((ParameterizedType) type).getActualTypeArguments(), Class[]::new)
                : new Class[0];
    }

}
